import 'package:dio/dio.dart';
import 'package:flutter_nest_life/constants/index.dart';
import 'package:flutter_nest_life/utils/TokenManager.dart';

import 'EventBus.dart';
import 'toast.dart';

class RequestDio {
  // 单例模式
  final Dio _dio = Dio();
  // 构造函数
  RequestDio() {
    _dio
      ..options.baseUrl = GlobalVariable.BASE_URL
      ..options.connectTimeout =
          const Duration(seconds: GlobalVariable.NET_WORK_TIME)
      ..options.receiveTimeout =
          const Duration(seconds: GlobalVariable.NET_WORK_TIME);
    _registerInterceptors();
  }
  // 封装请求方法
  // 1. get请求
  Future<dynamic> get(String url,
      {Map<String, dynamic>? queryParameters}) async {
    try {
      final result = await _dio.get(url, queryParameters: queryParameters);
      return _handleResult(result);
    } on DioException catch (e) {
      PromptAction.showError('网络请求失败: ${e.message}');
      throw Exception('网络请求失败: ${e.message}');
    }
  }

  // 2. post请求
  Future<dynamic> post(String url, {Map<String, dynamic>? data}) async {
    final result = await _dio.post(url, data: data);
    return _handleResult(result);
  }

  // 3. put请求
  Future<dynamic> put(String url, {Map<String, dynamic>? data}) async {
    final result = await _dio.put(url, data: data);
    return _handleResult(result);
  }

  // 4. delete请求
  Future<dynamic> delete(String url, {Map<String, dynamic>? data}) async {
    final result = await _dio.delete(url, data: data);
    return _handleResult(result);
  }

  // 5.upload 上传头像
  Future upload(String url, {FormData? data}) async {
    final result = await _dio.post(url, data: data);
    return _handleResult(result);
  }

  // 针对于所有的请求 - 统一处理返回的结果
  _handleResult(Response<dynamic> response) {
    if (response.data['code'] == GlobalVariable.SUCCESS_CODE) {
      // 请求的业务成功
      return response.data['data'];
    }
    // 请求业务失败 -> 抛出异常 (程序员) / 增加提示 (用户)
    //  增加提示
    PromptAction.showError(response.data['message']);
    throw Exception(response.data['message']);
  }

  // 判断refreshToken是否过期
  Future<bool> _isRefreshTokenExpired() async {
    // 1. 获取刷新token
    // 2. 发请求 -> /refreshToken
    // 3. 该请求中传入refreshToken
    // 4. 根据状态码 来判断token是否过期
    final refreshToken = tokenManager.getRefreshToken();
    // 注意点: 自己创建一个dio实例 -> 为了避免使用之前的dio实例发请求 (携带的token -> 过期的)
    Dio dio = Dio();
    try {
      final res = await dio.post(
          '${GlobalVariable.BASE_URL}${HTTP_PATH.REFRESH_TOKEN}',
          options: Options(headers: {'Authorization': 'Bearer $refreshToken'}));
      // 业务状态码 -> refreshToken有效
      if (res.data['code'] == 10000) {
        // 刷新token成功了
        // 将获取到的最新的token和refreshToken 进行存储
        tokenManager.saveToken(res.data['data']['token'],
            refreshToken: res.data['data']['refreshToken']);
        return true;
      } else {
        // refreshToken过期了
        return false;
      }
    } catch (e) {
      return false;
    }
  }

  // 注册拦截器
  _registerInterceptors() {
    _dio.interceptors.add(
      InterceptorsWrapper(
        // 请求拦截器：可以在此处添加一些请求前的处理逻辑，比如添加请求头、参数等。被发送之前，对请求进行处理。
        onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
          // 如果你想完成请求并返回一些自定义数据，你可以使用 `handler.resolve(response)`。
          // 如果你想终止请求并触发一个错误，你可以使用 `handler.reject(error)`。
          final token = tokenManager.getToken();
          if (token.isNotEmpty) {
            options.headers['Authorization'] = 'Bearer $token';
          }
          return handler.next(options);
        },
        // 响应拦截器：在返回响应数据之前，对响应数据进行处理
        onResponse: (Response response, ResponseInterceptorHandler handler) {
          // 检查 HTTP 状态码是否在 200-300 之间（表示成功），使用 ! 强制解包确保非空（需确保 response 不为 null）。
          if (response.statusCode! >= 200 && response.statusCode! <= 300) {
            // 如果你想终止请求并触发一个错误，你可以使用 `handler.reject(error)`。
            // 将成功响应传递给下一个拦截器或最终回调，保持响应数据原样传递。
            return handler.next(response);
          } else {
            PromptAction.showError(response.statusMessage!);
            // 构造 DioException 并终止请求链，错误会进入 onError 拦截器处理。
            return handler
                .reject(DioException(requestOptions: response.requestOptions));
          }
        },
        // 在请求失败时，对错误进行处理 - 异常处理
        onError: (DioException error, ErrorInterceptorHandler handler) async {
          if (error.response?.statusCode == 401) {
            final refreshToken = tokenManager.getRefreshToken();
            if (refreshToken.isNotEmpty) {
              bool isOk = await _isRefreshTokenExpired();
              if (isOk) {
                try {
                  final newOptions = error.requestOptions.copyWith(headers: {
                    ...error.requestOptions.headers,
                    'Authorization': 'Bearer ${tokenManager.getToken()}'
                  });
                  final res = await _dio.fetch(newOptions);
                  return handler.resolve(res);
                } catch (e) {
                  // 处理重新请求失败的情况
                  tokenManager.deleteToken();
                  eventBus.fire(LogoutEvent());
                }
              } else {
                tokenManager.deleteToken();
                eventBus.fire(LogoutEvent());
              }
            } else {
              tokenManager.deleteToken();
              eventBus.fire(LogoutEvent());
            }
          }
          return handler.next(error);
        },
      ),
    );
  }
}

final requestDio = RequestDio();
